package cn.redragon.soa.config.redis;

import cn.redragon.soa.common.core.exception.ObjectNotFoundException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheOperation;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import org.springframework.cache.interceptor.SimpleCacheResolver;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.ReflectionUtils;

public class EmptyCacheResolver extends SimpleCacheResolver {

    public EmptyCacheResolver(CacheManager cacheManager) {
        super(cacheManager);
    }

    @Override
    public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
        Collection<String> cacheNames = getCacheNames(context);
        if (cacheNames == null) {
            return Collections.emptyList();
        }
        ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer();
        EvaluationContext evaContext = new MethodBasedEvaluationContext(context.getOperation(), context.getMethod(), context.getArgs(), paramNameDiscoverer);
        Expression exp = (new SpelExpressionParser()).parseExpression(((CacheOperation) context.getOperation()).getKey());
        String key = exp.getValue(evaContext, String.class);
        Collection<Cache> result = new ArrayList<>(cacheNames.size());
        for (String cacheName : cacheNames) {
            Cache cache = getCacheManager().getCache(cacheName);
            if (cache == null) {
                throw new IllegalArgumentException("Cannot find cache named '" +
                        cacheName + "' for " + context.getOperation());
            }
            result.add(checkEmptyCache(cache, key));
        }
        return result;
    }

    protected String getCacheValueMethod() {
        return "getCode";
    }

    private Cache checkEmptyCache(Cache cache, String key) {
        Cache.ValueWrapper valueWrapper = cache.get(key);
        if (Objects.nonNull(valueWrapper)) {
            Object cacheValue = valueWrapper.get();
            Method cacheValueMethod = ReflectionUtils.findMethod(cacheValue.getClass(), getCacheValueMethod());
            if (Objects.nonNull(cacheValueMethod) && Objects.isNull(ReflectionUtils.invokeMethod(cacheValueMethod, cacheValue))) {
                throw new ObjectNotFoundException(cacheValue.getClass().getSimpleName(), key);
            }
        }
        return cache;
    }
}